﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using Db4objects.Db4o;
using Db4objects.Db4o.Linq;

namespace BLServer
{
    [ServiceContract]
    public interface IDataService
    {
        [OperationContract]
        Project GetProjects();

        [OperationContract]
        bool AddDeleteRenameProject(string loginToken, string projectID, bool isAddSubproject, string newProjectName);

        [OperationContract]
        string Login(string name, string password);

        [OperationContract]
        List<ProjectIssue> GetIssues(string loginToken);

        [OperationContract]
        ProjectIssue GetIssueByID(string loginToken, string issueID);

        [OperationContract]
        bool PostIssue(string loginToken, string projectID, string issueName, ProjectIssueType issueType, string firstDetailInfo);

        [OperationContract]
        bool UpdateIssueName(string loginToken, string issueID, string newIssueName);

        [OperationContract]
        bool UpdateOrAddPersonRoleToIssue(string loginToken, string issueID, string username, PersonIssueRole newRole);        

        [OperationContract]
        bool PostIssueDetail(string loginToken, string issueID, string detailInfo);

        [OperationContract]
        List<User> GetUsers();

        [OperationContract]
        bool AddUser(string login, string password);

        [OperationContract]
        bool UpdateUserNamePassword(string loginToken, string oldPassword, string newPassword, string newName);
    }

    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode=InstanceContextMode.Single)]
    public class DataService : IDataService
    {
        public Project GetProjects()
        {
            ServerBrain.Init();
            var pdList = from Project prj in ServerBrain.db where prj.IsHead select prj; //take headed project
            //var pdAll = from Project prj2 in ServerBrain.db select prj2; //debug
            Project pd = pdList.First<Project>();
            return pd;
        }

        public bool AddDeleteRenameProject(string loginToken, string projectID, bool isAddSubproject, string newProjectName)
        {
            ServerBrain.Init();
            if (ServerBrain.usersByID.ContainsKey(loginToken))
            {
                User currentUser = ServerBrain.usersByID[loginToken];
                var prjHead = from Project p in ServerBrain.db where p.IsHead select p;
                if (prjHead.Count<Project>() == 1)
                {
                    Project projHead = prjHead.First<Project>();
                    Project proj = projHead.GetSubprojectByIDRecurse(projectID);
                    if (proj != null)
                    {
                        //add new subproject
                        if ((isAddSubproject) && (newProjectName != null)) 
                        {
                            Project newProj = new Project(newProjectName, false);
                            if (proj.Subprojects == null) proj.Subprojects = new List<Project>();
                            proj.Subprojects.Add(newProj);
                            ServerBrain.db.Store(projHead); //store head project!
                            ServerBrain.db.Commit();
                            return true;
                        }
                        //delete this project, it's subprojects and all associated issues
                        else if ((!isAddSubproject) && (newProjectName == null) && (!proj.IsHead)) //we can't delete head project 
                        {
                            List<Project> allsubprojectsWithThis = proj.GetAllSubprojectsRecurse(true);
                            List<string> allIssueIds = proj.GetAllSubprojectIssueIDsRecurse(true, allsubprojectsWithThis);
                            var allAssocIssues = from ProjectIssue pIssue in ServerBrain.db
                                                 where allIssueIds.Contains(pIssue.ID)
                                                 select pIssue;
                            Project par = projHead.GetParentOfSubprojectRecurse(projectID);
                            if (par != null)
                            {
                                if (par.Subprojects.Remove(proj))
                                {
                                    ServerBrain.db.Delete(proj);
                                    foreach (ProjectIssue pi in allAssocIssues)
                                    {
                                        ServerBrain.db.Delete(pi);
                                    }
                                    ServerBrain.db.Store(projHead); //update head project and all subprojects
                                    ServerBrain.db.Commit();
                                    return true;
                                }
                                else return false;
                            }
                            else return false;
                        }
                        //rename this project
                        else if ((!isAddSubproject) && (newProjectName != null))
                        {
                            proj.Name = newProjectName;
                            ServerBrain.db.Store(projHead);
                            ServerBrain.db.Commit();
                            return true;
                        }
                        else return false;
                    }
                    else return false;
                }
                else return false;
            }
            else return false;
        }

        public string Login(string name, string password)
        {
            ServerBrain.Init();
            /*
            var pers_all = from User p in ServerBrain.db select p;
            var pers_needed = from User p in pers_all
                              where ((p.Name == name) && (p.Password == password))
                              select p;
            */
            var pers = from User p in ServerBrain.db
                       where ((p.Name == name) && (p.Password == password))
                       select p;
            if (pers.Count<User>() > 0)
            {
                //create credential token
                User per = pers.First<User>();
                string token = Guid.NewGuid().ToString();
                ServerBrain.usersByID.Add(token, per);
                return token;
            }
            else return null; //there is no such user in the database
        }

        public List<ProjectIssue> GetIssues(string loginToken)
        {
            ServerBrain.Init();
            if (ServerBrain.usersByID.ContainsKey(loginToken))
            {
                string userID = ServerBrain.usersByID[loginToken].ID;
                var issues = from ProjectIssue pi in ServerBrain.db
                             where pi.Persons != null && pi.Persons.ContainsKey(userID)
                             select pi;
                if (issues.Count<ProjectIssue>() > 0)
                {
                    List<ProjectIssue> piList = issues.ToList<ProjectIssue>();
                    return piList;
                }
                else return null; //there is no any associated issue
            }
            else return null; //there is no this token in list - no user - no issues
        }

        public ProjectIssue GetIssueByID(string loginToken, string issueID)
        {
            ServerBrain.Init();
            if (ServerBrain.usersByID.ContainsKey(loginToken))
            {
                string userID = ServerBrain.usersByID[loginToken].ID;
                var issues = from ProjectIssue pi in ServerBrain.db
                             where pi.ID == issueID && pi.Persons != null && pi.Persons.ContainsKey(userID)
                             select pi;
                if (issues.Count<ProjectIssue>() == 1)
                {
                    ProjectIssue needed_issue = issues.First<ProjectIssue>();
                    return needed_issue;
                }
                else return null;
            }
            else return null;
        }

        public bool PostIssue(string loginToken, string projectID,
            string issueName, ProjectIssueType issueType, string firstDetailInfo)
        {
            ServerBrain.Init();
            if (ServerBrain.usersByID.ContainsKey(loginToken))
            {
                User us = ServerBrain.usersByID[loginToken];
                ProjectIssue newIssue = new ProjectIssue(issueName);
                newIssue.Details = new List<ProjectIssueDetail>(); //empty list for the first time
                if (firstDetailInfo != null)
                {
                    ProjectIssueDetail firstDetail = new ProjectIssueDetail(DateTime.Now, firstDetailInfo);                    
                    newIssue.Details.Add(firstDetail);
                }
                newIssue.Persons = new Dictionary<string, PersonIssueRole>();
                newIssue.Persons.Add(us.ID, PersonIssueRole.Admin);
                newIssue.IssueType = issueType;
                try
                {
                    var prjHead = from Project p in ServerBrain.db where p.IsHead select p;
                    //var prj = from Project p in ServerBrain.db where p.ID == projectID select p;
                    if (prjHead.Count<Project>() == 1)
                    {                                                                                                
                        Project projHead = prjHead.First<Project>();
                        Project proj = projHead.GetSubprojectByIDRecurse(projectID);
                        if (proj != null)
                        {                                                        
                            //ServerBrain.db.Delete(proj);
                            if (proj.IssuesIDs == null) proj.IssuesIDs = new List<string>();
                            proj.IssuesIDs.Add(newIssue.ID);
                            ServerBrain.db.Store(newIssue);                            
                            //ServerBrain.db.Store(proj);
                            ServerBrain.db.Store(projHead);
                            ServerBrain.db.Commit();
                            return true;
                        }
                        else return false;
                    }
                    else return false;
                }
                catch (Exception ex)
                {
                    return false;
                }                
            }
            else return false;
        }

        public bool UpdateIssueName(string loginToken, string issueID, string newIssueName)
        {
            ServerBrain.Init();
            if (ServerBrain.usersByID.ContainsKey(loginToken))
            {
                string userID = ServerBrain.usersByID[loginToken].ID;
                var issues = from ProjectIssue pi in ServerBrain.db where pi.ID == issueID select pi;
                if (issues.Count<ProjectIssue>() == 1)
                {
                    ProjectIssue issue = issues.First<ProjectIssue>();
                    if ((issue.Persons != null) && (issue.Persons.ContainsKey(userID))
                        && (issue.Persons[userID] == PersonIssueRole.Admin))
                    {
                        issue.Name = newIssueName; //update issue name
                        try
                        {
                            ServerBrain.db.Store(issue);
                            ServerBrain.db.Commit();
                            return true;
                        }
                        catch
                        {
                            return false;
                        }
                    }
                    else return false;
                }
                else return false;
            }
            else return false;
        }

        public bool UpdateOrAddPersonRoleToIssue(string loginToken, string issueID, string username, PersonIssueRole newRole)
        {
            ServerBrain.Init();
            if (ServerBrain.usersByID.ContainsKey(loginToken))
            {
                string userID = ServerBrain.usersByID[loginToken].ID;
                var targerUser = from User targetus in ServerBrain.db where (targetus.Name == username) select targetus;
                if (targerUser.Count<User>() == 1)
                {
                    string targetUserID = targerUser.First<User>().ID;
                    var issues = from ProjectIssue pi in ServerBrain.db where pi.ID == issueID select pi;
                    if (issues.Count<ProjectIssue>() == 1)
                    {
                        ProjectIssue issue = issues.First<ProjectIssue>();
                        if ((issue.Persons != null) && (issue.Persons.ContainsKey(userID))
                            && (issue.Persons[userID] == PersonIssueRole.Admin))
                        {                            
                            try
                            {
                                if (issue.Persons.ContainsKey(targetUserID))
                                    issue.Persons[targetUserID] = newRole;
                                else issue.Persons.Add(targetUserID, newRole);
                                ServerBrain.db.Store(issue); //potential bug - previous line can save but this line can throw bug
                                ServerBrain.db.Commit();
                                return true;
                            }
                            catch
                            {
                                return false;
                            }
                        }
                        else return false;
                    }
                    else return false;
                }
                else return false;
            }
            else return false;
        }        

        public bool PostIssueDetail(string loginToken, string issueID, string detailInfo)
        {
            ServerBrain.Init();
            if (ServerBrain.usersByID.ContainsKey(loginToken))
            {
                string userID = ServerBrain.usersByID[loginToken].ID;
                var issues = from ProjectIssue pi in ServerBrain.db where pi.ID == issueID select pi;
                if (issues.Count<ProjectIssue>() == 1)
                {
                    ProjectIssue issue = issues.First<ProjectIssue>();
                    if ((issue.Persons != null) && (issue.Persons.ContainsKey(userID))
                        //&& ((issue.Persons[userID] == PersonIssueRole.Admin) ||
                        //(issue.Persons[userID] == PersonIssueRole.Reporter))
                        )
                    {
                        try
                        {
                            //ServerBrain.db.Delete(issue); //delete issue from db before adding new commment
                            
                            ProjectIssueDetail newDetail = new ProjectIssueDetail(DateTime.Now, detailInfo);
                            if (issue.Details == null) issue.Details = new List<ProjectIssueDetail>();
                            issue.Details.Add(newDetail);
                                                    
                            ServerBrain.db.Store(issue); //update issue with new detail
                            ServerBrain.db.Commit();
                            return true;
                        }
                        catch (Exception ex)
                        {
                            return false;
                        }
                    }
                    else return false;
                }
                else return false;
            }
            else return false;
        }

        public List<User> GetUsers()
        {
            ServerBrain.Init();
            var users = from User user in ServerBrain.db select user;
            List<User> uList = users.ToList<User>();
            List<User> uListPasswordless = new List<User>();
            foreach (User u in uList)
            {
                uListPasswordless.Add(User.PasswordlessClone(u));
                //u.Password = ""; //clear all passwords before send
            }
            return uList;
        }

        public bool AddUser(string login, string password)
        {
            ServerBrain.Init();
            var users = from User user in ServerBrain.db where (user.Name == login) select user;
            if (users.Count<User>() == 0)
            {
                User newuser = new User(login, password);
                try
                {
                    ServerBrain.db.Store(newuser);
                    ServerBrain.db.Commit();
                    return true;
                }
                catch (Exception ex)
                {
                    return false;
                }
            }
            else return false;
        }

        public bool UpdateUserNamePassword(string loginToken, string oldPassword, string newPassword, string newName)
        {
            ServerBrain.Init();
            if (ServerBrain.usersByID.ContainsKey(loginToken))
            {
                string userID = ServerBrain.usersByID[loginToken].ID;
                var users = from User user in ServerBrain.db where (user.ID == loginToken) select user;
                if (users.Count<User>() == 1)
                {
                    User neededUser = users.First<User>();
                    if (neededUser.Password == oldPassword)
                    {
                        neededUser.Password = newPassword;
                        if (newName != null) neededUser.Name = newName;
                        try
                        {
                            ServerBrain.db.Store(neededUser);
                            ServerBrain.db.Commit();
                            return true;
                        }
                        catch (Exception ex)
                        {
                            return false;
                        }
                    }
                    else return false;
                }
                else return false;
            }
            else return false;
        }
    }
}
